﻿using Common.ServiceDiscovery.Abstractions;
using Common.ServiceDiscovery.Model;
using Common.Tools;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Net;

namespace Common.Consul.Extensions
{
    public static class ApplicationBuilderExtensions
    {

        public static IApplicationBuilder UseConsulRegisterService(this IApplicationBuilder app, IConfiguration configuration)
        {
            try
            {
                ServiceDiscoveryOption serviceDiscoveryOption = new ServiceDiscoveryOption();
                configuration.GetSection("ServiceDiscovery").Bind(serviceDiscoveryOption);
                app.UseConsulRegisterService(serviceDiscoveryOption);
                app.UseHealthCheckMiddleware(serviceDiscoveryOption);
            }
            catch (Exception ex)
            {
                LogHelper.Error(ex);
            } 
            return app;
        }

        public static IApplicationBuilder UseConsulRegisterService(this IApplicationBuilder app, ServiceDiscoveryOption serviceDiscoveryOption)
        {
            var applicationLifetime = app.ApplicationServices.GetRequiredService<IApplicationLifetime>() ??
               throw new ArgumentException("Missing Dependency", nameof(IApplicationLifetime));

            if (string.IsNullOrEmpty(serviceDiscoveryOption.ServiceName))
                throw new ArgumentException("service name must be configure", nameof(serviceDiscoveryOption.ServiceName));
            if (string.IsNullOrEmpty(serviceDiscoveryOption.SelfHost))
                throw new ArgumentException("SelfHost must be configure", nameof(serviceDiscoveryOption.SelfHost));
            if (serviceDiscoveryOption.SelfPort <= 0)
                throw new ArgumentException("SelfPort must be configure", nameof(serviceDiscoveryOption.SelfPort));
            Uri address = new Uri($"http://{serviceDiscoveryOption.SelfHost}:{serviceDiscoveryOption.SelfPort}");
            if (address != null)
            {
                var registryInformation = app.AddTenant(serviceDiscoveryOption.ServiceName,
                    serviceDiscoveryOption.Version,
                    address,
                    serviceType: serviceDiscoveryOption.ServiceType,
                    healthCheckUri: serviceDiscoveryOption.HealthCheckUrl,
                    tags: new[] { $"urlprefix-/{serviceDiscoveryOption.ServiceName}"
                    });

                applicationLifetime.ApplicationStopping.Register(() =>
                {
                    app.RemoveTenant(registryInformation.Id);
                });
            }
            return app;
        }

        /// <summary>
        /// 设置心跳响应
        /// </summary>
        /// <param name="app"></param>
        /// <param name="serviceDiscoveryOption">参数</param>
        /// <returns></returns>
        public static void UseHealthCheckMiddleware(this IApplicationBuilder app, ServiceDiscoveryOption serviceDiscoveryOption)
        {
            if (string.IsNullOrEmpty(serviceDiscoveryOption.HealthCheckUrl))
                serviceDiscoveryOption.HealthCheckUrl = "/healthcheck";
            app.Map(serviceDiscoveryOption.HealthCheckUrl, applicationBuilder => applicationBuilder.Run(async context =>
            {
                //LogHelper.Info($"{serviceDiscoveryOption.SelfHost}:{serviceDiscoveryOption.SelfPort} is Health Check");
                context.Response.StatusCode = (int)HttpStatusCode.OK;
                await context.Response.WriteAsync("OK");
            }));
        }
        private static string GetServiceId(string serviceName, Uri uri)
        {
            return $"{serviceName}_{uri.Host.Replace(".", "_")}_{uri.Port}";
        }

        public static ServiceInformation AddTenant(this IApplicationBuilder app, string serviceName, string version, Uri uri, ServiceType serviceType = ServiceType.HTTP, string? healthCheckUri = null, IEnumerable<string>? tags = null)
        {
            if (app == null)
            {
                throw new ArgumentNullException(nameof(app));
            }

            var serviceRegistry = app.ApplicationServices.GetRequiredService<IServiceDiscovery>();
            var registryInformation = serviceRegistry.RegisterServiceAsync(serviceName, version, uri, serviceType, healthCheckUri, tags)
                .Result;

            return registryInformation;
        }

        public static bool RemoveTenant(this IApplicationBuilder app, string serviceId)
        {
            if (app == null)
            {
                throw new ArgumentNullException(nameof(app));
            }
            if (string.IsNullOrEmpty(serviceId))
            {
                throw new ArgumentNullException(nameof(serviceId));
            }

            var serviceRegistry = app.ApplicationServices.GetRequiredService<IServiceDiscovery>();
            return serviceRegistry.DeregisterServiceAsync(serviceId)
                .Result;
        }

        public static string AddHealthCheck(this IApplicationBuilder app, ServiceInformation registryInformation, Uri checkUri, ServiceType serviceType = ServiceType.HTTP, TimeSpan? interval = null, string notes = null)
        {
            if (app == null)
            {
                throw new ArgumentNullException(nameof(app));
            }
            if (registryInformation == null)
            {
                throw new ArgumentNullException(nameof(registryInformation));
            }

            var serviceRegistry = app.ApplicationServices.GetRequiredService<IServiceDiscovery>();
            string checkId = serviceRegistry.RegisterHealthCheckAsync(registryInformation.Name, registryInformation.Id, checkUri, serviceType, interval, notes)
                .Result;

            return checkId;
        }

        public static bool RemoveHealthCheck(this IApplicationBuilder app, string checkId)
        {
            if (app == null)
            {
                throw new ArgumentNullException(nameof(app));
            }
            if (string.IsNullOrEmpty(checkId))
            {
                throw new ArgumentNullException(nameof(checkId));
            }

            var serviceRegistry = app.ApplicationServices.GetRequiredService<IServiceDiscovery>();
            return serviceRegistry.DeregisterHealthCheckAsync(checkId)
                .Result;
        }
    }
}
